/*
 * This file is part of ELCube.
 *
 * ELCube is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ELCube is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with ELCube.  If not, see <https://www.gnu.org/licenses/>.
 */
package cn.nkpro.elcube.co.spel;

import cn.nkpro.elcube.co.easy.EasyCollection;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 数组集合的扩展函数
 */
@Component("SpELarray")
public class NkArraySpEL implements NkSpELInjection {
    public String join(Iterable<String> it){
        return String.join(",", it);
    }
    public String join(CharSequence... cs){
        return String.join(",", cs);
    }
    public String joining(CharSequence delimiter, CharSequence... cs){
        return String.join(delimiter, cs);
    }
    public String joining(CharSequence delimiter, Iterable<String> it){
        return String.join(delimiter, it);
    }
    public List<Object> sub(Object it,int limit){
        return sub(it,0,limit);
    }

    public Map<String,Object> mergeMap(Iterable<Map<String,Object>> it){
        Map<String,Object> map = new HashMap<>();
        if(it!=null)it.forEach(map::putAll);
        return map;
    }
    public Map<String,Object> mergeMap(Map<String,Object>... it){
        Map<String,Object> map = new HashMap<>();
        if(it!=null)Arrays.stream(it).forEach(map::putAll);
        return map;
    }

    @SuppressWarnings("all")
    public List<Object> sub(Object it,int skip,int limit){
        if(it==null){
            return null;
        }else if(it instanceof Iterable){
            List<Object> list = new ArrayList<>();
            AtomicInteger loop = new AtomicInteger(0);
            ((Iterable<Object>)it).forEach(o->{
                int index = loop.getAndAdd(1);
                if(index>=skip&&index<skip+limit) {
                    list.add(o);
                }
            });
            return list;
        } else if (it.getClass().isArray()) {
            List<Object> list = new ArrayList<>();
            Object[] values = (Object[]) it;
            for(int i=skip;i<values.length&&i<skip+limit;i++){
                list.add(values[i]);
            }
            return list;
        }
        return Collections.emptyList();
    }
    @SuppressWarnings("all")
    public Object sort(Object it){
        Stream stream = null;
        if(it==null){
            return null;
        }else if(it instanceof Collection){
            stream =  ((Collection) it).stream();
        }else if(it.getClass().isArray()){
            stream = Arrays.stream((Object[]) it);
        }
        if(stream!=null){
            return stream.sorted((e1,e2)->{
                if(e1==null){
                    return -1;
                }
                if(e2==null){
                    return 1;
                }
                if(e1 instanceof Comparable && e2 instanceof Comparable){
                    return ((Comparable) e1).compareTo(e2);
                }
                return e1.toString().compareTo(e2.toString());
            }).collect(Collectors.toList());
        }
        return it;
    }

    @SuppressWarnings("all")
    public Object sort(Object it, String fieldExp){
        if(it==null){
            return null;
        }else{
            return EasyCollection.from(it).stream().sorted((o1, o2) -> {
                Object e1 = StringUtils.isBlank(fieldExp)?o1.target():o1.get(fieldExp);
                Object e2 = StringUtils.isBlank(fieldExp)?o2.target():o2.get(fieldExp);
                if(e1==null){
                    return -1;
                }
                if(e2==null){
                    return 1;
                }
                if(e1 instanceof Comparable && e2 instanceof Comparable){
                    return ((Comparable) e1).compareTo(e2);
                }
                return e1.toString().compareTo(e2.toString());
            })
            .map(e->e.target())
            .collect(Collectors.toList());
        }
    }
}
